use crate::{
    block::Block,
    debug_log,
    size_class::{PageKind, SizeClass},
};

#[derive(Debug)]
pub struct Page {
    pub(crate) free: *mut Block,        // free list of available blocks
    pub(crate) local_free: *mut Block,  // thread-local free list
    pub(crate) thread_free: *mut Block, // free list for other threads
    pub(crate) capacity: usize,         // number of blocks that can fit
    pub(crate) block_size: usize,       // size of each block
    pub(crate) used: usize,             // number of blocks in use
    pub(crate) id: usize,               // unique page identifier
    pub(crate) in_full: bool,           // whether page is in full list
    pub(crate) reserved: bool,          // whether page is reserved
    pub(crate) area_start: *mut u8,     // start of the page area
    pub(crate) area_size: usize,        // size of the page area
    pub(crate) prev: *mut Page,         // previous page in list
    pub(crate) next: *mut Page,         // next page in list
    pub(crate) size_class: SizeClass,   // size class of the page
}

impl Page {
    pub fn new(area_start: *mut u8, area_size: usize, size_class: SizeClass, id: usize) -> Self {
        let block_size = size_class.block_size;
        let total_block_size = (block_size + std::mem::size_of::<Block>() + 15) & !15; // 16-byte align
        let capacity = area_size / total_block_size;

        debug_log!("📄 Creating new page [id={}]:", id);
        debug_log!("  ├─ Block size: {} bytes", block_size);
        debug_log!("  ├─ Area size: {} bytes", area_size);
        debug_log!("  ├─ Header size: {} bytes", std::mem::size_of::<Block>());
        debug_log!("  ├─ Total block size: {} bytes", total_block_size);
        debug_log!("  ├─ Capacity: {} blocks", capacity);
        debug_log!("  └─ Page kind: {:?}", size_class.page_kind);

        let mut page = match size_class.page_kind {
            PageKind::Large => {
                // For large pages, treat the entire page as one block
                debug_log!("  ├─ Large page - single block");
                debug_log!("  └─ Page kind: Large");

                let block = Block::init(area_start, area_size);
                Page {
                    id,
                    area_start,
                    area_size,
                    block_size: area_size,
                    size_class,
                    free: block,
                    local_free: std::ptr::null_mut(),
                    thread_free: std::ptr::null_mut(),
                    used: 0,
                    capacity: 1,
                    in_full: false,
                    reserved: false,
                    next: std::ptr::null_mut(),
                    prev: std::ptr::null_mut(),
                }
            }
            _ => {
                // Initialize blocks directly in the area
                unsafe {
                    let mut current = area_start;
                    let mut first_block = std::ptr::null_mut();

                    // Only initialize blocks that fit completely
                    let usable_size = area_size - (area_size % total_block_size);
                    let actual_capacity = usable_size / total_block_size;

                    for i in 0..actual_capacity {
                        // Ensure block is aligned
                        let aligned_current = (current as usize + 15) & !15;
                        let block = Block::init(aligned_current as *mut u8, block_size);
                        if i < actual_capacity - 1 {
                            (*block).next = Block::init(
                                (aligned_current + total_block_size) as *mut u8,
                                block_size,
                            );
                        }
                        if i == 0 {
                            first_block = block;
                        }
                        current = (aligned_current + total_block_size) as *mut u8;
                    }

                    Page {
                        id,
                        area_start,
                        area_size,
                        block_size,
                        size_class,
                        free: first_block,
                        local_free: std::ptr::null_mut(),
                        thread_free: std::ptr::null_mut(),
                        used: 0,
                        capacity: actual_capacity,
                        in_full: false,
                        reserved: false,
                        next: std::ptr::null_mut(),
                        prev: std::ptr::null_mut(),
                    }
                }
            }
        };

        debug_log!("  └─ Initialized free list: {:?}", page.free as *const u8);
        page
    }

    pub fn malloc(&mut self) -> Option<*mut u8> {
        match self.size_class.page_kind {
            PageKind::Large => {
                // For large pages, just return the entire area if not used
                if self.used == 0 {
                    debug_log!("\n🔍 Page[{}] malloc (large):", self.id);
                    debug_log!("  └─ Allocated entire page at {:?}", self.area_start);
                    self.used = 1;
                    Some(Block::get_data_ptr(self.free))
                } else {
                    None
                }
            }
            _ => {
                // Normal block allocation for small/medium pages
                if self.free.is_null() {
                    return self.malloc_generic();
                }

                unsafe {
                    let block = self.free;
                    self.free = (*block).next;
                    self.used += 1;
                    let ptr = Block::get_data_ptr(block);

                    debug_log!("\n🔍 Page[{}] malloc:", self.id);
                    debug_log!("  ├─ Block at: {:?}", ptr);
                    debug_log!("  └─ Used: {}/{}", self.used, self.capacity);

                    Some(ptr)
                }
            }
        }
    }

    fn malloc_generic(&mut self) -> Option<*mut u8> {
        debug_log!("\n  Generic malloc analysis:");
        debug_log!("  ├─ Current used: {}", self.used);
        debug_log!("  ├─ Free list: {:?}", self.free);
        debug_log!("  └─ Local free list: {:?}", self.local_free);

        if !self.local_free.is_null() {
            debug_log!("\n🔄 Transferring from local_free");
            self.free = self.local_free;
            self.local_free = std::ptr::null_mut();
            return self.malloc();
        }
        None
    }

    fn count_free_list(&self, mut head: *mut Block) -> usize {
        let mut count = 0;
        unsafe {
            while !head.is_null() {
                count += 1;
                head = (*head).next;
            }
        }
        count
    }

    pub fn deallocate(&mut self, ptr: *mut u8) -> bool {
        if !self.contains_ptr(ptr) {
            return false;
        }

        match self.size_class.page_kind {
            PageKind::Large => {
                // For large pages, just mark as unused
                if self.used == 1 && ptr == self.area_start {
                    debug_log!("\n🔍 Page[{}] deallocate (large):", self.id);
                    debug_log!("  └─ Freed entire page");
                    self.used = 0;
                    true
                } else {
                    false
                }
            }
            _ => {
                // Normal block deallocation for small/medium pages
                let block = Block::from_data_ptr(ptr);

                unsafe {
                    self.used -= 1;
                    (*block).next = self.local_free;
                    self.local_free = block;

                    debug_log!("\n🔍 Page[{}] deallocate:", self.id);
                    debug_log!("  ├─ Block at: {:?}", ptr);
                    debug_log!("  ├─ Used: {}/{}", self.used, self.capacity);
                    debug_log!(
                        "  └─ Local free blocks: {}",
                        self.count_free_list(self.local_free)
                    );
                }

                true
            }
        }
    }

    pub fn contains_ptr(&self, ptr: *mut u8) -> bool {
        let ptr_addr = ptr as usize;
        let start_addr = self.area_start as usize;
        let end_addr = start_addr + self.area_size;

        match self.size_class.page_kind {
            PageKind::Large => {
                // For large pages, pointer must be exactly at start
                ptr_addr == start_addr
            }
            _ => {
                // For small/medium pages, check if within area
                ptr_addr >= start_addr && ptr_addr < end_addr
            }
        }
    }

    pub fn is_full(&self) -> bool {
        self.free.is_null()
    }

    pub fn is_empty(&self) -> bool {
        self.used == 0 && self.local_free.is_null() && self.thread_free.is_null()
    }

    pub fn set_next(&mut self, next: *mut Page) {
        self.next = next;
        unsafe {
            if !next.is_null() {
                (*next).prev = self;
            }
        }
    }

    pub fn set_prev(&mut self, prev: *mut Page) {
        self.prev = prev;
        unsafe {
            if !prev.is_null() {
                (*prev).next = self;
            }
        }
    }

    pub fn unlink(&mut self) {
        unsafe {
            if !self.prev.is_null() {
                (*self.prev).next = self.next;
            }
            if !self.next.is_null() {
                (*self.next).prev = self.prev;
            }
            self.prev = std::ptr::null_mut();
            self.next = std::ptr::null_mut();
        }
    }
}

impl std::fmt::Display for Page {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "Page[{}] {{size: {}b, used: {}/{}, lists: [free:{}, local:{}, thread:{}]{}{}}}",
            self.id,
            self.block_size,
            self.used,
            self.capacity,
            if self.free.is_null() { "×" } else { "✓" },
            if self.local_free.is_null() {
                "×"
            } else {
                "✓"
            },
            if self.thread_free.is_null() {
                "×"
            } else {
                "✓"
            },
            if self.in_full { " [FULL]" } else { "" },
            if self.reserved { " [RESERVED]" } else { "" }
        )
    }
}

unsafe impl Send for Page {}
unsafe impl Sync for Page {}
